package client

import (
	"encoding/binary"
	"gitee.com/Gogo-gitee/gogo-cache/protocol"
	"github.com/smallnest/goframe"
	"google.golang.org/protobuf/proto"
	_ "google.golang.org/protobuf/reflect/protopath"
	"log"
	"net"
)

const (
	MAGIC_NUM        = 125
	VERSION          = 1
	REQUEST_TYPE_GET = 1
	REQUESR_TYPE_SET = 2
)

var (
	ClientMap   = make(map[string]*Client)
	RegistryTab = New(3, nil)
)

type Client struct {
	Conn    net.Conn
	Address string
	Network string
	Fc      goframe.FrameConn
}

//NewClient new client
func NewClient(network, address string) (error, *Client) {
	conn, err := net.Dial(network, address)
	if err != nil {
		panic(err)
	}
	encoderConfig := goframe.EncoderConfig{
		ByteOrder:                       binary.BigEndian,
		LengthFieldLength:               4,
		LengthAdjustment:                0,
		LengthIncludesLengthFieldLength: false,
	}

	decoderConfig := goframe.DecoderConfig{
		ByteOrder:           binary.BigEndian,
		LengthFieldOffset:   0,
		LengthFieldLength:   4,
		LengthAdjustment:    0,
		InitialBytesToStrip: 4,
	}

	fc := goframe.NewLengthFieldBasedFrameConn(encoderConfig, decoderConfig, conn)
	return nil, &Client{
		Address: address,
		Network: network,
		Conn:    conn,
		Fc:      fc,
	}
}
func (c *Client) Close() {
	err := c.Conn.Close()
	if err != nil {
		panic(err)
	}
}

//Get get request
func (c *Client) Get(key string) {
	r := protocol.Request{
		RequestType: REQUEST_TYPE_GET,
		GetRequest:  &protocol.Get{Key: key},
		MagicNum:    MAGIC_NUM,
		Version:     VERSION,
	}
	marshal, err := proto.Marshal(&r)
	log.Printf("%+v\n", r)
	if err != nil {
		log.Fatalf("serialize failed")
	}
	err = c.Fc.WriteFrame(marshal)
	if err != nil {
		log.Fatalf("request failed")
	}
}

//Set set request
func (c *Client) Set(key string, value []byte) {
	r := protocol.Request{
		RequestType: REQUESR_TYPE_SET,
		SetRequest:  &protocol.Set{Key: key, Value: value},
		MagicNum:    MAGIC_NUM,
		Version:     VERSION,
	}
	marshal, err := proto.Marshal(&r)
	log.Printf("%+v\n", r)
	if err != nil {
		log.Fatalf("serialize failed")
	}
	err = c.Fc.WriteFrame(marshal)
	if err != nil {
		log.Fatalf("request failed")
	}
}

// Receive receive response from channel,send struct{} to exit channel for closing receiving response
func (c *Client) Receive(exit chan struct{}) chan protocol.Response {
	responses := make(chan protocol.Response)
	go func() {
		for {
			select {
			case <-exit:
				return
			default:
				buf, err := c.Fc.ReadFrame()
				if err != nil {
					log.Fatalf("read failed")
					return
				}
				var rsp protocol.Response
				err = proto.Unmarshal(buf, &rsp)
				if err != nil {
					log.Fatalf("unserized failed")
					return
				}
				responses <- rsp
			}
		}

	}()
	return responses
}
